﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
using Marmat.Forms.Skin;

namespace Marmat.Forms
{
    [DefaultEvent("ValueChanged")]
    [MarmatToolboxItem(false)]
    public partial class EditControl : System.Windows.Forms.UserControl, IControl, IBindingField, IBindingFieldNotification
    {
        protected EditControl()
        {

            // 此调用是设计器所必需的。
            this.InitializeComponent();

            // 在 InitializeComponent() 调用之后添加任何初始化。
            this.SetStyle(ControlStyles.ContainerControl, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true); // 尺寸变化后绘制
            this.SetStyle(ControlStyles.Opaque, false); // 改善绘图质量
            this.SetStyle(ControlStyles.SupportsTransparentBackColor, true); // 使控件支持透明色
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            // 初始化参数
            this.ShowCaption = true;
            this.BackColor = Color.White;
            base.BackColor = Color.Transparent;
        }

        protected DevExpress.XtraEditors.BaseEdit BaseEdit
        {
            get => this.m_BaseEdit;
            set => this.m_BaseEdit = value;
        }

        private DevExpress.XtraEditors.BaseEdit _m_BaseEdit;

        protected internal DevExpress.XtraEditors.BaseEdit m_BaseEdit
        {
            [MethodImpl(MethodImplOptions.Synchronized)]
            get => _m_BaseEdit;
            [MethodImpl(MethodImplOptions.Synchronized)]
            set
            {
                if (_m_BaseEdit != null)
                {
                    _m_BaseEdit.Enter -= this._m_BaseEdit_Enter;
                    _m_BaseEdit.Leave -= this._m_BaseEdit_Leave;
                    _m_BaseEdit.MouseEnter -= this._m_BaseEdit_MouseEnter;
                    _m_BaseEdit.MouseLeave -= this._m_BaseEdit_MouseLeave;
                    _m_BaseEdit.CustomDisplayText -= m_BaseEdit_CustomDisplayText;
                    _m_BaseEdit.EditValueChanging -= m_BaseEdit_EditValueChanging;
                    _m_BaseEdit.EditValueChanged -= m_BaseEdit_EditValueChanged;
                }

                _m_BaseEdit = value;
                if (_m_BaseEdit != null)
                {
                    _m_BaseEdit.Enter += this._m_BaseEdit_Enter;
                    _m_BaseEdit.Leave += this._m_BaseEdit_Leave;
                    _m_BaseEdit.MouseEnter += this._m_BaseEdit_MouseEnter;
                    _m_BaseEdit.MouseLeave += this._m_BaseEdit_MouseLeave;
                    _m_BaseEdit.CustomDisplayText += m_BaseEdit_CustomDisplayText;
                    _m_BaseEdit.EditValueChanging += m_BaseEdit_EditValueChanging;
                    _m_BaseEdit.EditValueChanged += m_BaseEdit_EditValueChanged;
                }
            }
        }

        private bool enter = false;
        private bool mouseEnter = false;
        private void _m_BaseEdit_Leave(object sender, EventArgs e)
        {
            enter = false;
            this.Invalidate();
        }

        private void _m_BaseEdit_Enter(object sender, EventArgs e)
        {
            enter = true;
            this.Invalidate();
        }

        private void _m_BaseEdit_MouseLeave(object sender, EventArgs e)
        {
            mouseEnter = false;
            this.Invalidate();
        }

        private void _m_BaseEdit_MouseEnter(object sender, EventArgs e)
        {
            mouseEnter = true;
            this.Invalidate();
        }

        /// <summary>
        /// 获取编辑器的基类组件。
        /// </summary>
        /// <returns></returns>
        [Browsable(false)]
        public DevExpress.XtraEditors.BaseEdit BaseControl => this.BaseEdit;

        /// <summary>
        /// 获取一个值，用以指示 System.ComponentModel.Component 当前是否处于设计模式。
        /// </summary>
        /// <value></value>
        /// <returns>如果 System.ComponentModel.Component 处于设计模式，则为 true；否则为 false。</returns>
        /// <remarks></remarks>
        [Browsable(false)]
        public new bool DesignMode => base.DesignMode;

        /// <summary>
        /// 获取或设置编辑器的边框样式。
        /// </summary>
        /// <returns></returns>
        [DefaultValue(typeof(EditControlBorderStyles), "Default")]
        public new EditControlBorderStyles BorderStyle
        {
            get => this._BorderStyle;
            set
            {
                this._BorderStyle = value;
                if (value == EditControlBorderStyles.Default)
                    this.BaseEdit.BorderStyle = DevExpress.XtraEditors.Controls.BorderStyles.Default;
                else
                    this.BaseEdit.BorderStyle = DevExpress.XtraEditors.Controls.BorderStyles.NoBorder;
                this.CalcHeight();
                this.CalcEditControlLocation();
            }
        }

        private EditControlBorderStyles _BorderStyle = EditControlBorderStyles.Default;


        /// <summary>
        /// 获取或设置控件的背景色。
        /// </summary>
        /// <value></value>
        /// <returns></returns>
        /// <remarks></remarks>
        [DefaultValue(typeof(Color), "White")]
        public new Color BackColor
        {
            get => this._BackColor;
            set
            {
                this._BackColor = value;
                if (this.BaseEdit is not null)
                {
                    if (value == Color.White)
                        this.BaseEdit.ResetBackColor();
                    else
                        this.BaseEdit.BackColor = value;
                    this.Invalidate();
                }
            }
        }

        private Color _BackColor = Color.White;
        private bool _ShowCaption = true;
        /// <summary>
        /// 获取或设置是否显示标题。
        /// </summary>
        /// <value></value>
        /// <returns></returns>
        /// <remarks></remarks>
        [DefaultValue(true)]
        [Category("标题")]
        [Description("设置或返回是否显示控件标题的文本。")]
        public virtual bool ShowCaption
        {
            get => this._ShowCaption;
            set
            {
                this._ShowCaption = value;
                this.CalcEditControlLocation();
            }
        }

        private string _Caption;

        /// <summary>
        /// 设置或返回控件标题的文本。
        /// </summary>
        [DefaultValue(null)]
        [Category("标题")]
        [Description("设置或返回控件标题的文本。")]
        public string Caption
        {
            get => this._Caption;
            set
            {
                this._Caption = value;
                if (this.ShowCaption == true)
                {
                    this.Invalidate();
                }
            }
        }

        internal ContentAlignment _CaptionTextAlign;

        /// <summary>
        /// 获取或设置控件显示的标题的显示位置。
        /// </summary>
        /// <returns></returns>
        [Category("标题")]
        [Description("获取或设置控件显示的标题的显示位置。")]
        public virtual ContentAlignment CaptionTextAlign
        {
            get
            {
                if (this._CaptionTextAlign == default(int))
                    return ContentAlignment.MiddleRight;
                return this._CaptionTextAlign;
            }
            set => this._CaptionTextAlign = value;
        }

        private int _CaptionWidth = 75;
        /// <summary>
        /// 设置或返回标题文本的宽度。
        /// </summary>
        /// <value></value>
        /// <returns></returns>
        /// <remarks></remarks>
        [DefaultValue(75)]
        [Category("标题")]
        [Description("设置或返回标题文本的宽度。")]
        public int CaptionWidth
        {
            get => this._CaptionWidth;
            set
            {
                if (value >= 0)
                {
                    this._CaptionWidth = value;
                    this.CalcEditControlLocation();
                }
            }
        }

        private Font _CaptionFont;
        /// <summary>
        /// 获取或设置控件显示的标题的字体。
        /// </summary>
        /// <returns></returns>
        //[DefaultValue(typeof(Font), "微软雅黑,9.0pt")]
        [Category("标题")]
        [Description("获取或设置控件显示的标题的字体。")]
        public Font CaptionFont
        {
            get
            {
                if (this._CaptionFont is null)
                    return this.Font;
                return this._CaptionFont;
            }
            set
            {
                this._CaptionFont = value;
                this.Invalidate();
            }
        }

        internal Color _CaptionForeColor = default;
        /// <summary>
        /// 获取或设置控件显示的标题的字体颜色。
        /// </summary>
        /// <returns></returns>
        [Category("标题")]
        [Description("获取或设置控件显示的标题的字体颜色。")]
        public Color CaptionForeColor
        {
            get
            {
                if (this._CaptionForeColor == default)
                    return this.BaseEdit.ForeColor;
                return this._CaptionForeColor;
            }
            set
            {
                this._CaptionForeColor = value;
                this.Invalidate();
            }
        }

        private Color _CaptionBackColor = Color.Transparent;
        /// <summary>
        /// 获取或设置控件显示的标题的背景颜色。
        /// </summary>
        /// <returns></returns>
        [DefaultValue(typeof(Color), "Transparent")]
        [Category("标题")]
        [Description("获取或设置控件显示的标题的背景颜色。")]
        public Color CaptionBackColor
        {
            get => this._CaptionBackColor;
            set
            {
                this._CaptionBackColor = value;
                this.Invalidate();
            }
        }


        /// <summary>
        /// 获取或设置编辑器的值。
        /// </summary>
        /// <returns></returns>
        [DefaultValue(null)]
        public object Value
        {
            get
            {
                var value = this._value;
                if (this.BaseEdit is not null)
                {
                    value = this.BaseEdit.EditValue;
                }
                return value;
            }
            set
            {
                this._value = value;
                this.BaseEdit.EditValue = value;
            }
        }

        private object _value;

        /// <summary>
        /// 获取或设置表示编辑器空值的文本。
        /// </summary>
        /// <returns></returns>
        [DefaultValue("")]
        [Category("行为")]
        [Description("获取或设置表示编辑器空值的文本。")]
        public virtual string NullText
        {
            get => this.BaseEdit.Properties.NullText;
            set => this.BaseEdit.Properties.NullText = value;
        }

        /// <summary>
        /// 获取或设置一个值，该值指示文本框中的文本是否为只读。
        /// </summary>
        /// <value></value>
        /// <returns></returns>
        /// <remarks></remarks>
        [DefaultValue(typeof(BooleanEnum), "Default")]
        [Category("行为")]
        [Description("获取或设置字符，该字符用于屏蔽单行 System.Windows.Forms.TextBox 控件中的密码字符。")]
        public virtual BooleanEnum ReadOnly
        {
            get => this.m_ReadOnly;
            set
            {
                this.m_ReadOnly = value;
                switch (value)
                {
                    case BooleanEnum.False:
                        this.IsReadOnly = false;
                        break;
                    case BooleanEnum.Default:
                        if (this.BindingField.DataSource is not null && !string.IsNullOrEmpty(this.BindingField.DataMember))
                            this.IsReadOnly = !this.BindingField.DataSource.AllowEdit || this.BindingField.DataSource.GetColumnCollection().FirstOrDefault(item => string.Equals(item.Name, this.BindingField.DataMember)).ReadOnly;
                        else
                            this.IsReadOnly = false;
                        break;
                    case BooleanEnum.True:
                        this.IsReadOnly = true;
                        break;
                }
                this.Invalidate();
            }
        }

        private BooleanEnum m_ReadOnly = BooleanEnum.Default;

        /// <summary>
        /// 绘制
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if (this.BaseEdit is null)
                return;
            int x = this.BaseEdit.Left - 1;
            int w = this.Width - x - 1;
            using var pen = new Pen(Color.FromArgb(70, 5, 27, 50));
            if (this.BorderStyle == EditControlBorderStyles.Underline)
            {
                e.Graphics.DrawLine(pen, this.BaseEdit.Left, this.BaseEdit.Bottom, this.BaseEdit.Right, this.BaseEdit.Bottom);
            }
            if (this.BorderStyle == EditControlBorderStyles.Style1)
            {
                var rect = new Rectangle(this.BaseEdit.Left - 1, this.BaseEdit.Top - 1, this.BaseEdit.Width + 1, this.BaseEdit.Height + 1);
                if (!this.IsReadOnly && (enter || mouseEnter))
                {
                    if (enter && !mouseEnter)
                        pen.Color = Color.FromArgb(52, 143, 228);
                    e.Graphics.DrawRectangle(pen, rect);
                }
                else if (this.DesignMode)
                {
                    pen.DashStyle = DashStyle.DashDot;
                    e.Graphics.DrawRectangle(pen, rect);
                }
            }
            // 绘制标题文字==============================================================
            if (this.ShowCaption)
            {
                string caption = this.Caption;
                if (string.IsNullOrEmpty(caption) && this.BindingField.DataSource is object && !string.IsNullOrEmpty(this.BindingField.DataMember))
                {
                    var column = this.BindingField.DataSource.GetColumnCollection().FirstOrDefault(item => string.Equals(item.Name, this.BindingField.DataMember));
                    if (column is not null)
                    {
                        caption = (string.IsNullOrEmpty(column.Caption) ? column.Name : column.Caption) + ":";
                    }
                }
                if (!string.IsNullOrEmpty(caption))
                {
                    e.Graphics.SmoothingMode = SmoothingMode.Default;
                    e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault;
                    var flags = TextFormatFlags.TextBoxControl;
                    var rectangle = new Rectangle(0, 0, this.CaptionWidth, this.Height);
                    switch (this.CaptionTextAlign)
                    {
                        case ContentAlignment.TopLeft:
                            rectangle = new Rectangle(0, 0, this.CaptionWidth, this.Height);
                            break;
                        case ContentAlignment.TopCenter:
                            flags |= TextFormatFlags.HorizontalCenter;
                            break;
                        case ContentAlignment.TopRight:
                            flags |= TextFormatFlags.Right;
                            rectangle = new Rectangle(0, 0, this.CaptionWidth, this.Height);
                            break;
                        case ContentAlignment.MiddleLeft:
                            flags |= TextFormatFlags.VerticalCenter;
                            break;
                        case ContentAlignment.MiddleCenter:
                            flags |= TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter;
                            break;
                        case ContentAlignment.MiddleRight:
                            flags |= TextFormatFlags.VerticalCenter | TextFormatFlags.Right;
                            break;
                        case ContentAlignment.BottomLeft:
                            flags |= TextFormatFlags.Bottom;
                            break;
                        case ContentAlignment.BottomCenter:
                            flags |= TextFormatFlags.Bottom | TextFormatFlags.HorizontalCenter;
                            break;
                        case ContentAlignment.BottomRight:
                            flags |= TextFormatFlags.Bottom | TextFormatFlags.Right;
                            break;
                    }
                    var foreColor = this.BaseEdit.ForeColor;
                    if (this.CaptionForeColor != default)
                    {
                        foreColor = this.CaptionForeColor;
                    }
                    TextRenderer.DrawText(e.Graphics, caption, this.CaptionFont, rectangle, foreColor, this.CaptionBackColor, flags);
                }
            }
        }

        /// <summary>
        /// 获取或设置控件内的空白。
        /// </summary>
        /// <returns>表示控件的内部间距特征的 System.Windows.Forms.Padding。</returns>
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new Padding Padding
        {
            get => base.Padding;
            set => base.Padding = value;
        }

        internal BindingField _BindingField = null;
        /// <summary>
        /// 返回或设置控件的绑定Table字段
        /// </summary>
        /// <returns></returns>
        [Localizable(true)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Category("数据")]
        [DisplayName("(BindingField)")]
        [Description("返回或设置控件的绑定Table字段")]
        public virtual BindingField BindingField
        {
            get
            {
                if (this._BindingField is null)
                    this._BindingField = new BindingField(this.BaseEdit, "EditValue", null, null, true, DataSourceUpdateMode.OnValidation, this);
                return this._BindingField;
            }
        }

        private void m_BaseEdit_CustomDisplayText(object sender, DevExpress.XtraEditors.Controls.CustomDisplayTextEventArgs e)
        {
            if (this.DesignMode)
            {
                if (this.BindingField.DataSource is object && !string.IsNullOrEmpty(this.BindingField.DataMember))
                {
                    string dataSource;
                    if (this.BindingField.DataSource is Component component)
                        dataSource = component.Site.Name;
                    else
                        dataSource = this.BindingField.DataSource.ToString();
                    e.DisplayText = dataSource + "." + this.BindingField.DataMember;
                }
            }
            else
            {
                var args = new CustomDisplayTextEventArgs(e.Value, e.DisplayText);
                CustomDisplayText?.Invoke(this, args);
                e.DisplayText = args.DisplayText;
            }
        }

        private void m_BaseEdit_EditValueChanging(object sender, DevExpress.XtraEditors.Controls.ChangingEventArgs e)
        {
            var arg = new ChangingEventArgs(e.OldValue, e.NewValue, e.Cancel);
            ValueChanging?.Invoke(this, arg);
            e.Cancel = arg.Cancel;
            e.NewValue = arg.NewValue;
        }

        private void m_BaseEdit_EditValueChanged(object sender, EventArgs e)
        {
            this._value = this.BaseEdit.EditValue;
            ValueChanged?.Invoke(this, EventArgs.Empty);
        }

        void IBindingFieldNotification.NotifyPropertyChanged()
        {
            // 在ReadOnly属性为Default时,需要根据动态得来的状态决定是否需要设为只读
            if (this.ReadOnly == BooleanEnum.Default)
            {
                if (this.BindingField.DataSource is not null && !string.IsNullOrEmpty(this.BindingField.DataMember))
                    this.IsReadOnly = !this.BindingField.DataSource.AllowEdit || this.BindingField.DataSource.GetColumnCollection().FirstOrDefault(item => string.Equals(item.Name, this.BindingField.DataMember)).ReadOnly;
                else
                    this.IsReadOnly = false;
            }
        }

        protected virtual bool IsReadOnly
        {
            get => this.BaseEdit.ReadOnly;
            set => this.BaseEdit.ReadOnly = value;
        }

        protected override void OnValidated(EventArgs e)
        {
            base.OnValidated(e);
            if (this.BindingField.IsBinding)
            {
                this.BindingField?.ReadValue();
            }
        }

        protected override void OnFontChanged(EventArgs e)
        {
            base.OnFontChanged(e);
            this.CaptionWidth = this.CaptionWidth;
            this.BaseEdit.Properties.Appearance.Font = base.Font;
            this.CalcHeight();
        }

        protected override void OnForeColorChanged(EventArgs e)
        {
            base.OnForeColorChanged(e);
            this.BaseEdit.Properties.Appearance.ForeColor = base.ForeColor;
        }


        /// <summary>
        /// 重新计算编辑器控件的位置
        /// </summary>
        protected void CalcEditControlLocation()
        {
            int left = 0;
            int top = 0;
            int right = 0;
            int bottom = 0;
            if (this.ShowCaption)
                left = this.CaptionWidth;
            switch (this.BorderStyle)
            {
                case EditControlBorderStyles.Underline:
                    bottom = 1;
                    break;
                case EditControlBorderStyles.Style1:
                    top = 1;
                    left++;
                    right = 1;
                    bottom = 1;
                    break;
            }
            this.Padding = new Padding(left, top, right, bottom);
            this.Invalidate();
        }
        /// <summary>
        /// 重新计算控件的高度。
        /// </summary>
        protected virtual void CalcHeight()
        {
            switch (this.BorderStyle)
            {
                case EditControlBorderStyles.Default:
                    this.Height = this.BaseEdit.Height;
                    break;
                case EditControlBorderStyles.Underline:
                    this.Height = this.BaseEdit.Height + 1;
                    break;
                case EditControlBorderStyles.NoBorder:
                    this.Height = this.BaseEdit.Height;
                    break;
                case EditControlBorderStyles.Style1:
                    this.Height = this.BaseEdit.Height + 2;
                    break;
                default:
                    break;
            }
        }
    }
}